import numpy as np
import cv2
import time
import os
from threading import Thread
from static.offset_py_files.utils import *
############################
import logging

my_normal_logger = logging.getLogger('django')


############################

class offsetDetect:
    def __init__(self,
                 camera_ip,
                 cap,
                 save_dir,
                 result_len=10,
                 thres=0.8,
                 MIN_MATCH_COUNT=10,
                 BEST_DISTANCE=7,
                 GOOD_DISTANCE=30,
                 date_position_y=90,
                 texts=("完全偏移", "严重偏移", "轻微偏移", "无偏移")):
        self.camera_ip = camera_ip
        self.cap = cap
        self.save_dir = save_dir
        self.results = []
        self.result_len = result_len
        self.thres = thres
        self.alarm_state = True
        self.is_offset = False

        self.MIN_MATCH_COUNT = MIN_MATCH_COUNT
        self.BEST_DISTANCE = BEST_DISTANCE
        self.GOOD_DISTANCE = GOOD_DISTANCE
        self.algorithms_all = {
            "SIFT": cv2.SIFT_create(),
            # "SURF": cv2.xfeatures2d.SURF_create(8000),
            "ORB": cv2.ORB_create()
        }

        self.texts = texts
        self.date_position_y = date_position_y
        self.pre_frame = None

    def offset_init(self, frame):
        self.alarm_state = True
        self.pre_frame = frame
        ip_str = self.camera_ip.replace('.', '_')
        my_normal_logger.info('Camera offset initial')
        raw_pic_path = '{}/raw/{}_raw.jpg'.format(self.save_dir, ip_str)
        thread_raw = Thread(target=save_pic, args=(self.pre_frame, raw_pic_path))
        thread_raw.daemon = True
        thread_raw.start()

    def detect(self, frame):
        if self.pre_frame is None:
            self.offset_init(frame)

        image1 = self.pre_frame[self.date_position_y:]
        image2 = frame[self.date_position_y:]

        result = self.match2frames(image1, image2)
        res = 0 if result == 3 else 1
        self.update(res)
        # text_frame = putText(frame, self.texts[result])
        h5_offset_pic_path = None
        if result == 3:
            self.pre_frame = frame
        else:
            my_normal_logger.info('Offset Situation -->' + self.camera_ip + ' ' + self.texts[result])
            if self.is_offset:
                date_time = time.strftime('%Y-%m-%d', time.localtime(time.time()))
                # time_str = time.strftime('%m%d-%H-%M-%S', time.localtime(time.time()))
                ip_str = self.camera_ip.replace('.', '_')
                # ip_str = '8479849645'

                offset_pic_path = '{}/{}/{}/{}_{}.jpg'.format(self.save_dir, 'offset', ip_str, 'offset', date_time)
                h5_offset_pic_path = '/static/img_data/offset/{}/{}_{}.jpg'.format(ip_str, 'offset', date_time)

                thread_offset = Thread(target=save_pic, args=(frame, offset_pic_path))
                thread_offset.daemon = True
                thread_offset.start()

                # thread_offset2 = Thread(target=save_pic, args=(self.pre_frame, offset_pic_raw))
                # thread_offset2.daemon = True
                # thread_offset2.start()

            # offect_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            # print('!!!', offect_time, self.texts[result])

        return [self.is_offset, h5_offset_pic_path]

    def match2frames(self, image1, image2):
        img1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
        img2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

        size1 = img1.shape
        size2 = img2.shape

        img1 = cv2.resize(img1, (int(size1[1] * 0.3), int(size1[0] * 0.3)), cv2.INTER_LINEAR)
        img2 = cv2.resize(img2, (int(size2[1] * 0.3), int(size2[0] * 0.3)), cv2.INTER_LINEAR)

        sift = self.algorithms_all["SIFT"]

        kp1, des1 = sift.detectAndCompute(img1, None)
        kp2, des2 = sift.detectAndCompute(img2, None)

        FLANN_INDEX_KDTREE = 0
        index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
        search_params = dict(checks=50)

        flann = cv2.FlannBasedMatcher(index_params, search_params)

        matches = flann.knnMatch(np.asarray(des1, np.float32), np.asarray(des2, np.float32), k=2)

        # 过滤
        good = []
        for m, n in matches:
            if m.distance < 0.7 * n.distance:
                good.append(m)

        # print('len good:', len(good))
        if len(good) <= self.MIN_MATCH_COUNT:
            return 0  # 完全偏移
        else:
            distance_sum = 0  # 特征点2d物理坐标偏移总和
            for m in good:
                distance_sum += self.get_distance(kp1[m.queryIdx].pt, kp2[m.trainIdx].pt)
            distance = distance_sum / len(good)  # 单个特征点2D物理位置平均偏移量

            # print('distance:', distance)
            if distance < self.BEST_DISTANCE:
                return 3  # 无偏移
            elif distance < self.GOOD_DISTANCE and distance >= self.BEST_DISTANCE:
                return 2  # 轻微偏移
            else:
                return 1  # 严重偏移

    # 计算2D物理距离
    def get_distance(self, p1, p2):
        x1, y1 = p1
        x2, y2 = p2
        return np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)

    def update(self, result):
        if not self.alarm_state:
            self.results = []
            self.is_offset = False
        else:
            self.results.append(result)
            if len(self.results) > self.result_len:
                self.results = self.results[-self.result_len:]

            if sum(self.results) >= self.result_len * self.thres:
                my_normal_logger.info('Alarm ' + str(sum(self.results)) + str(self.results))
                self.is_offset = True
                self.alarm_state = False
